/*
 * Simple program to modify PXA27x GPIOs
 *
 * Copyright (C) 2006, CompuLab, Ltd, by Mike Rapoport (mike@compulab.co.il)
 *
 * Based on devmem2.c: Copyright (C) 2000, Jan-Derk Bakker (jdb@lartmaker.nl)
 *
 * Pieces are taken from Linux kernel:
 * linux/include/asm-arm/arch-pxa/pxa-regs.h
 * linux/include/asm-arm/arch-pxa/hardware.h
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

/* 
 * This program demonstrates GPIO access on PXA2xx processors.
 * It can be used as stand-alone utility for GPIO modifications, or as
 * an example for programatical access of GPIO from user-space
 * application.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>

#define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
  __LINE__, __FILE__, errno, strerror(errno)); exit(1); } while(0)

#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)

unsigned long virt_base = 0;

/* Macros for PXA reigsters and bit field access */
#define __REG(x)	(*(volatile unsigned long*)((x)+virt_base))
#define __REG2(x,y)	\
	(*(volatile unsigned long *)((unsigned long)&__REG(x) + (y)))

#define GPLR0	__REG(0x000)  /* GPIO Pin-Level Register GPIO<31:0> */
#define GPLR1	__REG(0x004)  /* GPIO Pin-Level Register GPIO<63:32> */
#define GPLR2	__REG(0x008)  /* GPIO Pin-Level Register GPIO<80:64> */
#define GPLR3	__REG(0x100)  /* GPIO Pin-Level Register GPIO<80:64> */

#define GPDR0	__REG(0x00C)  /* GPIO Pin Direction Register GPIO<31:0> */
#define GPDR1	__REG(0x010)  /* GPIO Pin Direction Register GPIO<63:32> */
#define GPDR2	__REG(0x014)  /* GPIO Pin Direction Register GPIO<80:64> */
#define GPDR3	__REG(0x10C)  /* GPIO Pin Direction Register GPIO<127:96> */

#define GPSR0	__REG(0x018)  /* GPIO Pin Output Set Register GPIO<31:0> */
#define GPSR1	__REG(0x01C)  /* GPIO Pin Output Set Register GPIO<63:32> */
#define GPSR2	__REG(0x020)  /* GPIO Pin Output Set Register GPIO<80:64> */
#define GPSR3	__REG(0x118)  /* GPIO Pin Output Set Register GPIO<127:96> */

#define GPCR0	__REG(0x024)  /* GPIO Pin Output Clear Register GPIO<31:0> */
#define GPCR1	__REG(0x028)  /* GPIO Pin Output Clear Register GPIO<63:32> */
#define GPCR2	__REG(0x02C)  /* GPIO Pin Output Clear Register GPIO<80:64> */
#define GPCR3	__REG(0x124)  /* GPIO Pin Output Clear Register GPIO<127:96> */

#define GPIO_bit(x)	(1 << ((x) & 0x1f))

#define _GPLR(x)	__REG2(0x000, ((x) & 0x60) >> 3)
#define _GPDR(x)	__REG2(0x00C, ((x) & 0x60) >> 3)
#define _GPSR(x)	__REG2(0x018, ((x) & 0x60) >> 3)
#define _GPCR(x)	__REG2(0x024, ((x) & 0x60) >> 3)

#define GPLR(x)		(*((((x) & 0x7f) < 96) ? &_GPLR(x) : &GPLR3))
#define GPDR(x)		(*((((x) & 0x7f) < 96) ? &_GPDR(x) : &GPDR3))
#define GPSR(x)		(*((((x) & 0x7f) < 96) ? &_GPSR(x) : &GPSR3))
#define GPCR(x)		(*((((x) & 0x7f) < 96) ? &_GPCR(x) : &GPCR3))

#define SET_GPIO		0
#define CLEAR_GPIO		1
#define READ_GPIO		2
#define GPDR_IN			3
#define GPDR_OUT		4

/** 
   cmd  specifies type of GPIO access
   gpio is the numebr of GPIO that should be accesed
   This routine performs actual GPIO access and sets/reads its
   direction and value.
   The macros used to access bit fields of GPIO control registers are
   the same as in linux kernel source, so this routine can be used
   inside kernel device driver.
*/
int gpio_access(unsigned int cmd, unsigned int gpio)
{
	switch(cmd) {
		case SET_GPIO:	/*  Set GPIO level to high */
			GPSR(gpio) = GPIO_bit(gpio);
			break;
		case CLEAR_GPIO:/* Set GPIO level to low */
			GPCR(gpio) = GPIO_bit(gpio);
			break;
		case READ_GPIO:	/* Read GPIO level */
			return(GPLR(gpio) & GPIO_bit(gpio));
			break;
		case GPDR_IN:	/* Set pin to function as input GPIO */
			GPDR(gpio) &= ~GPIO_bit(gpio);
			break;
		case GPDR_OUT:	/* Set pin to function as output GPIO */
			GPDR(gpio) |= GPIO_bit(gpio);
			break;
		default:
			return -EINVAL;
	}
	return 0;
}

const char* commands[] = {
	[SET_GPIO] = "SET",
	[CLEAR_GPIO] = "CLEAR",
	[READ_GPIO] = "READ",
	[GPDR_IN] = "IN",
	[GPDR_OUT] = "OUT",
};

int main(int argc, char *argv[])
{
	int fd;
	unsigned int gpio;
	unsigned int cmd = ~0;
	int i, ret;
	off_t target = 0x40e00000; /* base address for GPIO registers */

	if(argc != 3) {
		fprintf(stderr, "\nUsage:\t%s <GPIO> <command>\n"
			"\tGPIO    : GPIO number\n"
			"\tcommand : can be one of the following:\n"
			"\t\tSET   - Set GPIO level to high\n"
			"\t\tCLEAR - Set GPIO level to low\n"
			"\t\tREAD  - Read GPIO level\n"
			"\t\tIN    - Set pin to function as input GPIO\n"
			"\t\tOUT   - Set pin to function as output GPIO\n",
			argv[0]);
		return 1;
	}

	/* get the GPIO number */
	gpio = strtoul(argv[1], 0, 0);

	/* parse the command */
	for ( i = 0; i < strlen(argv[2]); i++ )
		argv[2][i] = toupper(argv[2][i]);
	for ( i = 0; i < sizeof(commands)/sizeof(commands[0]); i++ ) {
		if ( strcmp(argv[2], commands[i]) == 0 ) {
			cmd = i;
			break;
		}
	}

	if ( cmd == ~0 ) {
		fprintf(stderr, "%s: command %s undefined\n", argv[0], argv[2]);
		return 1;
	}

	/* Open /dev/mem and map memory page containing GPIO registers */
	if( (fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1 )
		FATAL;

	virt_base = (unsigned long)mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target & ~MAP_MASK);
	if ( virt_base == -1) 
		FATAL;

	/* perform the GPIO access */
	ret = gpio_access(cmd, gpio);
	/* if the command was read - print the value */
	if ( cmd == READ_GPIO ) {
		printf("GPIO %d value: %d\n", gpio, (ret >> (gpio & 0x1f)));
	}

	/* Unmap memory and free file descriptor */
	if ( munmap((void*)virt_base, MAP_SIZE) == -1 )
		FATAL;
	close(fd);

	return 0;
}

